Programacion AVR analog input (ADC)

Contenido

Configurar y utilizar los puertos de entrada analógica del microcontrolador.

Descripcion

En el caso del Atmega168pa tenemos 6 puertos que nos permiten hacer conversión analógico-digital:

Código de ejemplo ADC

Para utilizar el ADC (Analog Digital Converter) tenemos que realizar lo siguiente:

Seleccionar puerto para el ADC

El convertidor analógico-digital a pesar de tener varios puertos disponibles solo puede usar uno a la vez (si queremos obtener la entrada analógica en varios puertos tenemos que hacer multiplexación).

Para cambiar el puerto sobre el que se obtienen las lecturas se hace a traves de los 4 bits menos significativos del registro ADMUX, en este caso no es una mascara, sino que es el numero del puerto en sí (ya que solo puede ser uno el puerto activo para el ADC).

Para indicar el puerto podemos hacer un OR con el número del puerto (o macro del puerto) pero tenemos que dejar los 4 bits más significativos sin cambiar.

Para activar el puerto PC3 del microcontrolador como puerto para el ADC lo haríamos tal que así:

ADMUX = (0xf0 & ADMUX) | PC3;

Esta operación nos permite mantener los 4 bits más significativos sin alterar y dejar a 0 los 4 menos significativos, para poder hacer un OR del puerto que queramos y que no intefiera con alguna valor previo.

Al hacer un OR con el puerto 3 "00000011" solo se cambian los bits menos significativos con el valor binario "3"

Divisor de voltaje

Al añadir la resistencia para realizar el divisor de voltaje en el circuito tenemos que tener en cuenta cual es el rango de valores en los que se mueve nuestro sensor.

Si por ejemplo tenemos un sensor que en su rango minimo nos da una resistencia de 10K Ohm y la resistencia que usamos para el divisor de voltaje es de 10K Ohm en este caso la lectura más baja que nos dará el ADC será de 512 (ya que tenemos 10K Ohm en las dos partes del divisor el votaje será la mitad), en este caso tendríamos que considerar usar una resistencia mas grande para el divisor de voltaje para poder obtener un rango de lectura mayor.

Modo Free-Run e Interrupciones

Modo Free-Run

Por defecto el conversor ADC trabaja en modo Single-shot, nosotros iniciamos el momento en el que hacer una lectura, otro de los modos de funcionamiento es el free-run, en este modo el conversor ADC está constantemente haciendo lecturas, nosotros solo tenemos que leer el valor ADC para obtener la ultima actualización.

Para usar el modo free-run solo tenemos que activar el auto-trigger del ADC (el modo free-run es el modo por defecto del auto-trigger):

ADCSRA |= (1 << ADATE);

A pesar de que no tenemos que indicar cuando queremos hacer cada lectura si que tenemos que indicar una primera lectura para que el modo free-run empiece a hacer lecturas constantes:

ADCSRA |= (1 << ADSC);

Interrupciones con lecturas del ADC

Podemos hacer que se genere una interrupción cada vez que se complete una lectura del ADC, activamos la interrupciones del ADC:

ADCSRA |= (1 << ADIE);

Y activamos las interrupciones globales:

sei();

Definimos la ISR() que se ejecutará al lanzarse la interrupción:

ISR(ADC_vect) {}

A continuación se muestra un código de ejemplo que usa el modo free-run y las interrupciones al completarse cada lectura del ADC:

#include <avr/io.h>
#include <avr/power.h>
#include <avr/interrupt.h>

volatile uint8_t ledValue;
volatile uint16_t adcValue;

void ADC_init() {
    ADMUX |= (1 << REFS0);
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1);
    ADCSRA |= (1 << ADEN);
    ADCSRA |= (1 << ADIE); // Activamos interrupciones para el ADC
    ADCSRA |= (1 << ADATE); // Activamos auto trigger del ADC (Por defecto el modo es el free-run)

    ADCSRA |= (1 << ADSC); // Iniciamos la primera conversión (a partir de aqui el free-run se ejecutara de manera automatica)

    sei();
}

ISR(ADC_vect) {
    adcValue = ADC; // Leemos el valor del ADC

    ledValue = (adcValue >> 7);

    PORTB = 0;

    for (uint8_t i = 0; i <= ledValue; i++) {
      PORTB |= (1 << i);
    }

    // Clear the interrupt flag to allow next conversion
    ADCSRA |= (1 << ADIF);
}

int main(void) {
    clock_prescale_set(clock_div_1);

    DDRB = 0xFF; //Todos los puertos del registro B como salida

    ADC_init();

    while (1) {

    }
}
Tags

AVR | microcontrolador | Analog